package typage;

import java.util.*;
import ast.*;

class SymbolTable {

    // toutes les classes: String -> JmmClass
    static HashMap classes = new HashMap();

    static void add_new_class(JmmClass c) {
	classes.put(c.class_id, c);
	c.attributes = new HashMap();
	c.constructors = new HashSet();
	c.methods = new HashMap();
    }

    // la classe Object
    static JmmClass jmmObject = 
	new JmmClass(new Loc(0,0), "Object", new Ident(null, "Object"),
		     new FieldList());
    static { 
	add_new_class(jmmObject);
	jmmObject.super_class = jmmObject;
	jmmObject.constructors.add(new Profile(new String[0]));
    }
			      
    // test de sous-classe
    static boolean subclass_aux(JmmClass a, JmmClass b) {
	if (a == b) return true;
	if (a == jmmObject) return false;
	JmmClass sa = a.super_class;
	return sa != null && subclass_aux(sa, b);
    }

    static boolean subclass(String a, String b) {
	return subclass_aux((JmmClass)classes.get(a), 
			    (JmmClass)classes.get(b));
    }

    // test de sous-typage
    static boolean subtype(String t1, String t2) {
	if (Misc.base_type(t1)) 
	    return (Misc.base_type(t2) && t1.equals(t2));
	if (Misc.base_type(t2))
	    return false;
	if (t1.equals("typenull"))
	    return (!t2.equals("typenull"));
	return subclass(t1, t2);
    }

    // test de compatibilite
    static boolean compatible(String t1, String t2) {
	return subtype(t1, t2) || subtype(t2, t1);
    }
    
    // maximum de deux types, en supposant la compatibilite
    static String max_type(String t1, String t2) {
	return subtype(t1, t2) ? t2 : t1;
    }

    // table donnant le type de retour d'une methode
    // la methode est ici indiquee par son nom long (C_m_t1_..._t_n)
    static HashMap method_ty = new HashMap();
    
    // noms longs des methodes et des constructeurs
    static String method_name(String m, Profile p) {
	String s = m;
	for (int i = 0; i < p.l.length; i++) s = s + "_" + p.l[i];
	return s;
    }

    static String method_name(String c, String m, VarList l) {
	String s = c + "_" + m;
	for (Iterator it = l.iterator(); it.hasNext();)
	    s = s + "_" + ((Var)it.next()).var_ty.ty_val;
	return s;
    }

    static String constructor_name(String c, VarList l) {
	String s = "_new_" + c;
	for (Iterator it = l.iterator(); it.hasNext();)
	    s = s + "_" + ((Var)it.next()).var_ty.ty_val;
	return s;
    }

    // recherche d'un attribut
    static Attribute find_attribute(String s, String id) {
	JmmClass c = (JmmClass)classes.get(s);
	while (c != jmmObject) {
	    HashMap attr = c.attributes;
	    Attribute v = (Attribute)attr.get(id);
	    if (v != null) return v;
	    c = c.super_class;
	}
	return null;
    }

    // primitives: String -> CallPrimitive
    static HashMap primitives = new HashMap();
    static void add_primitive(String id, String ty, Profile p) {
	primitives.put(id, new CallPrimitive(p, ty)); 
    }
    static CallPrimitive get_primitive(String id) {
	return (CallPrimitive)primitives.get(id);
    }
    static { 
	add_primitive("open_screen", "unit",
		      new Profile(new String[] { "int", "int" }));
	add_primitive("clear_screen", "unit",
		      new Profile(new String [] {}));
	add_primitive("set_color", "unit",
		      new Profile(new String[] { "int", "int", "int" }));
	add_primitive("draw_point", "unit",
		      new Profile(new String[] { "int", "int" }));
	add_primitive("draw_line", "unit",
		      new Profile(new String[] { "int","int","int","int" }));
	add_primitive("draw_circle", "unit",
		      new Profile(new String[] { "int", "int", "int" }));
	add_primitive("draw_rect", "unit",
		      new Profile(new String[] { "int","int","int","int" }));
	add_primitive("fill_rect", "unit",
		      new Profile(new String[] { "int","int","int","int" }));
	add_primitive("usleep", "unit",
		      new Profile(new String[] { "int" }));
	add_primitive("get", "int",
		      new Profile(new String[] { }));
	add_primitive("set", "unit",
		      new Profile(new String[] { "int" }));
	add_primitive("move", "bool",
		      new Profile(new String[] { }));
	add_primitive("turn_left", "unit",
		      new Profile(new String[] { }));
	add_primitive("turn_right", "unit",
		      new Profile(new String[] { }));
	
    }
    static boolean is_primitive(String id) { 
	return primitives.containsKey(id); 
    }

}

